// Test for SExp

#include <gtest/gtest.h>
#include "sexp.h"
#include "smem.h"
#include "ssym.h"
#include "sstring.h"
#include "sutil.h"

#ifndef	FALSE
#define	FALSE	(0)
#define	TRUE	(1)
#endif

#define	EQ(s1, s2)		EXPECT_TRUE(eq(s1, s2))
#define	NEQ(s1, s2)		EXPECT_FALSE(eq(s1, s2))
#define	EQUAL(s1, s2)	EXPECT_TRUE(equal(s1, s2))
#define	NIL(s)			EXPECT_TRUE(nilp(s))


const int HEAP_SIZE = 1024 * 1024;
static unsigned long s_buf[HEAP_SIZE / sizeof(unsigned long)];

class SExpTest : public testing::Test {
protected:
	virtual void SetUp() {
		smem_init(s_buf, sizeof(s_buf), NULL);
		sexp_new();
	}

	virtual void TearDown() {
		sexp_delete();
	}
};

static std::ostream& operator<<(std::ostream& ostrm, SExp s) {
	return ostrm;
}


//=============================================================================

TEST_F(SExpTest, Eq) {
	SExp a = fixnum2s(1);
	SExp d = fixnum2s(2);

	EQ(a, a) << "eq on fixnum";
	NEQ(a, d) << "neq on fixnum";

	SExp s1 = cons(a, d);
	SExp s2 = cons(a, d);
	EQ(s1, s1) << "eq on cons cell";
	NEQ(s1, s2) << "neq on cons cell";
}

TEST_F(SExpTest, Consp) {
	SExp a = fixnum2s(1);
	SExp d = fixnum2s(2);
	SExp s = cons(a, d);
	EXPECT_TRUE(consp(s)) << "test cons cell";
	EXPECT_FALSE(consp(a)) << "test fixnum";
}

TEST_F(SExpTest, Cons) {
	SExp a = fixnum2s(1);
	SExp d = fixnum2s(2);
	SExp s = cons(a, d);
	EQ(car(s), a) << "car of cell";
	EQ(cdr(s), d) << "cdr of cell";
}

TEST_F(SExpTest, Replace) {
	SExp a = fixnum2s(1);
	SExp d = fixnum2s(2);
	SExp r = fixnum2s(3);
	SExp s = cons(a, d);

	rplaca(s, r);
	EQ(car(s), r) << "rplaca replace car";
	EQ(cdr(s), d) << "rplaca doesn't affect cdr";
	rplacd(s, r);
	EQ(cdr(s), r) << "rplacd replace cdr";
}

TEST_F(SExpTest, Fixnum) {
	SExp s = fixnum2s(1);
	EXPECT_EQ(type_of(s), tFixnum) << "fixnum";
}

TEST_F(SExpTest, Symbolp) {
	SExp s = intern("symbol");
	EXPECT_TRUE(symbolp(s)) << "intern is symbol";
}

TEST_F(SExpTest, Symbol) {
	SExp s1 = intern("symbol");
	SExp s2 = intern("symbol");
	EQ(s1, s2) << "2 intern symbols are equal";
}

TEST_F(SExpTest, Nil) {
	EXPECT_TRUE(symbolp(nil)) << "nil is symbol";
	EQ(nil, intern("nil")) << "nil is 'nil'";
	EXPECT_FALSE(consp(nil)) << "nil isn't cons";
}


//=============================================================================
// sutil

TEST_F(SExpTest, eqaul) {
	SExp s1 = cons(fixnum2s(1), nil);
	SExp s2 = cons(fixnum2s(1), nil);
	NEQ(s1, s2) << "same cons cells are not eq";
	EQUAL(s1, s2) << "same cons cells are equal";

	SExp str1 = gen_str("test", -1);
	SExp str2 = gen_str("test", -1);
	NEQ(str1, str2) << "same strings are not eq";
	EQUAL(str1, str2) << "same strings are equal";
}

TEST_F(SExpTest, list1) {
	SExp s1 = list1(fixnum2s(1));
	SExp s2 = cons(fixnum2s(1), nil);
	EQUAL(s1, s2) << "list1" << s1 << "," << s2;
}

TEST_F(SExpTest, list2) {
	SExp s1 = list2(fixnum2s(1), fixnum2s(2));
	SExp s2 = cons(fixnum2s(1), cons(fixnum2s(2), nil));
	EQUAL(s1, s2) << "list2" << s1 << "," << s2;
}

TEST_F(SExpTest, list3) {
	SExp s1 = list3(fixnum2s(1), fixnum2s(2), fixnum2s(3));
	SExp s2 = cons(fixnum2s(1), cons(fixnum2s(2), cons(fixnum2s(3), nil)));
	EQUAL(s1, s2) << "list3" << s1 << "," << s2;
}

TEST_F(SExpTest, copy_list) {
	SExp s1 = list3(fixnum2s(1), fixnum2s(2), fixnum2s(3));
	SExp s2 = copy_list(s1);
	NEQ(s1, s2) << "copied list is not eq to the original one";
	EQUAL(s1, s2) << "copied list is equal to the original one";

#if 0
	SExp sorg = list3(fixnum2s(1), fixnum2s(2), fixnum2s(3));
	rplaca(s1, intern("sym"));
	EQUAL(s2, sorg) << "change original list don't affect copied list";

	SExp s4 = cons(fixnum2s(1), cons(fixnum2s(2), fixnum2s(3)));
	SExp s5 = cons(fixnum2s(1), cons(fixnum2s(2), fixnum2s(3)));
	SExp s6 = copy_list(s4);
	EQUAL(s4, s6) << "can copy dotted list";
#endif
}

TEST_F(SExpTest, list_length) {
	SExp s = list3(fixnum2s(1), fixnum2s(2), fixnum2s(3));
	EXPECT_EQ(list_length(s), 3) << "list length";

	EXPECT_EQ(list_length(nil), 0) << "nil's length == 0";

	SExp d = cons(fixnum2s(1), fixnum2s(2));
	EXPECT_EQ(list_length(d), 1) << "dotted list length";
}

TEST_F(SExpTest, last1) {
	SExp d = cons(fixnum2s(3), fixnum2s(4));
	SExp s = cons(fixnum2s(1), cons(fixnum2s(2), d));
	EQ(last1(s), d) << "last1 is eq to last cell";

	EQ(last1(nil), nil) << "last1(nil) is nil";

	EQ(last1(fixnum2s(1)), fixnum2s(1)) << "last1(1) is 1";
}

TEST_F(SExpTest, nreverse) {
	SExp s1 = list3(fixnum2s(1), fixnum2s(2), fixnum2s(3));
	SExp s2 = list3(fixnum2s(3), fixnum2s(2), fixnum2s(1));
	SExp s3 = copy_list(s1);
	SExp r = nreverse(s1);
	EQUAL(r, s2) << "nreverse reverse list";
}

TEST_F(SExpTest, assoc) {
	SExp e1 = cons(intern("a"), fixnum2s(1));
	SExp e2 = cons(intern("b"), fixnum2s(2));
	SExp e3 = cons(intern("c"), fixnum2s(3));
	SExp s = list3(e1, e2, e3);
	EQUAL(assoc(intern("b"), s), e2) << "assoc find by key";

	NIL(assoc(intern("z"), s)) << "if assoc can't find, it returns nil";
}

TEST_F(SExpTest, mapcar1) {
	struct Local {
		static SExp f(SExp s, void* ud) {
			return list1(s);
		}
	};

	SExp s = list3(fixnum2s(1), fixnum2s(2), fixnum2s(3));
	SExp r = mapcar1(Local::f, s, NULL);
	SExp d = list3(list1(fixnum2s(1)), list1(fixnum2s(2)), list1(fixnum2s(3)));
	EQUAL(r, d) << "mapcar1 map all element";

	EQUAL(mapcar1(Local::f, nil, NULL), nil) << "mapcar1 can handle '()";
}

